package com.contact.util.util.search;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.BeanUtils;
import org.joda.time.LocalDate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.contact.util.util.collections.Utils;
import com.contact.util.util.filters.DateDataFilter;
import com.contact.util.util.filters.DateRange;
import com.contact.util.util.filters.DateRangeDataFilter;
import com.contact.util.util.filters.SearchDataFilter;
import com.contact.util.util.filters.WildCardDataFilter;
import com.trg.search.Filter;

/**
 * This class encapsulates helper functions for creating and transforming Filter related objects, for UI and DTO
 * support.
 */
public final class FilterHelper {

    private static final Logger logger = LoggerFactory.getLogger(FilterHelper.class);

    private static final String REPLACE_KEY = "%s";
    public static final Map<String, Filter> SPECIAL_FILTER_MAP = Utils.newHashMap();

    private FilterHelper() {
        //singleton
    }

    public static Object getFilterValue(Collection<Filter> filters, String property) {
        Filter filter = getFilter(filters, property);
        return filter == null ? null : filter.getValue();
    }

    public static Filter getFilter(Collection<Filter> filters, String property) {
        return getFilter(filters, property, -1);
    }

    public static Filter getFilter(Collection<Filter> filters, String property, int operator) {
        if (Utils.hasValues(filters)) {
            for (Filter filter : filters) {
                if (filter.getProperty().equals(property) && (operator == -1 ? true : filter.getOperator() == operator)) {
                    return filter;
                }
            }
        }
        return null;
    }

    public static List<Filter> buildFilter(DateDataFilter<?> dateFilter) {
        return buildDateFilters(dateFilter.getKey(), dateFilter.getFilterValue());
    }

    public static List<Filter> buildFilter(DateRangeDataFilter rangeFilter) {
        return buildDateFilters(rangeFilter.getKey(), rangeFilter.getFilterValue());
    }

    public static List<Filter> buildDateFilters(String property, DateRange dateRange) {
        List<Filter> dateRangeFilters = Utils.newArrayList();
        LocalDate fromDate = dateRange.getFromDate();
        LocalDate toDate = dateRange.getToDate();
        if (fromDate != null) {
            dateRangeFilters.add(new Filter(property, fromDate, Filter.OP_GREATER_OR_EQUAL));
        }
        if (toDate != null) {
            dateRangeFilters.add(new Filter(property, toDate, Filter.OP_LESS_OR_EQUAL));
        }
        return dateRangeFilters;
    }

    public static Filter buildFilter(SearchDataFilter searchFilter) {
        Collection<WildCardDataFilter> wildCardFilters = searchFilter.getFilterValue().getFilters();
        if (Utils.hasValues(wildCardFilters)) {
            List<Filter> filters = Utils.newArrayList();
            for (WildCardDataFilter wildCardFilter : wildCardFilters) {
                filters.add(buildFilter(wildCardFilter));
            }
            return Filter.or(filters.toArray(new Filter[] {}));
        }
        return null;
    }

    public static Filter buildFilter(WildCardDataFilter filter) {
        Filter specialFilter = SPECIAL_FILTER_MAP.get(filter.getKey());
        if (specialFilter != null) {
            return createFilterInstance(specialFilter, filter.getFilterValue());
        }
        return new Filter(filter.getKey(), filter.getFilterValue(), Filter.OP_ILIKE);
    }

    /*public static <E extends Enum<E>> Filter buildFilter(EnumDataFilter<E> enumFilter) {
        Enum<E> enumValue = enumFilter.getFilterValue();
        if (enumValue instanceof EnumGroup<?>) {
            return buildEnumGroupFilter(enumFilter);
        }
        return buildFilter((DataFilter) enumFilter);
    }

    public static Filter buildFilter(DataFilter dataFilter) {
        Object value = dataFilter.getFilterValue();
        if (value instanceof EnumGroup<?>) {
            return buildEnumGroupFilter(dataFilter);
        }
        return new Filter(dataFilter.getKey(), value, value == null ? Filter.OP_NULL : Filter.OP_EQUAL);
    }

    private static Filter buildEnumGroupFilter(DataFilter dataFilter) {
        Collection<? extends Enum<?>> enums = ((EnumGroup<?>) dataFilter.getFilterValue()).getActualEnums();
        //If there are more than one Enum in the collection create an "Equals" filter, else create and "In" filter.
        if (enums.size() == 1) {
            return new Filter(dataFilter.getKey(), enums.iterator().next(), Filter.OP_EQUAL);
        } else {
            return new Filter(dataFilter.getKey(), enums, Filter.OP_IN);
        }
    }

    public static Filter buildFilter(EmptyValueDataFilter emptyValueDataFilter) {
        EmptyValueStatus evs = emptyValueDataFilter.getDisplayValue();
        if (evs == null) {
            return null;
        }

        if (EmptyValueStatus.NOT_EMPTY.equals(evs)) {
            return Filter.isNotNull(emptyValueDataFilter.getKey());
        }

        return Filter.isNull(emptyValueDataFilter.getKey());

    }*/

    @SuppressWarnings("unchecked")
    public static Filter createFilterInstance(Filter templateFilter, String value) {
        Filter clone = null;

        try {
            clone = (Filter) BeanUtils.cloneBean(templateFilter);
            Object obj = templateFilter.getValue();
            if (obj instanceof Filter) {
                clone.setValue(createFilterInstance((Filter) obj, value));
            } else if (obj instanceof List) {
                List<Filter> cloneList = Utils.newArrayList();
                for (Filter f : (List<Filter>) obj) {
                    Filter listClone = createFilterInstance((Filter) BeanUtils.cloneBean(f), value);
                    cloneList.add(listClone);
                }
                clone.setValue(cloneList);
            } else if (obj instanceof String) {
                String val = (String) clone.getValue();
                clone.setValue(val.replace(REPLACE_KEY, value));
            }
        } catch (Exception e) {
            logger.error("Error creating a new filter from template " + templateFilter.getProperty(), e);
        }
        return clone;
    }

}
